package com.daffodil.storage.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import com.daffodil.storage.client.SeaweedfsClient;
import com.daffodil.storage.client.SeaweedfsClient.DirEntrie;
import com.daffodil.storage.client.SeaweedfsClient.DirFile;
import com.daffodil.storage.client.SeaweedfsClient.DirPath;
import com.daffodil.storage.client.SeaweedfsClient.DirResult;
import com.daffodil.storage.exception.BaseException;
import com.daffodil.storage.exception.SeaweedfsException;
import com.daffodil.storage.model.Page;
import com.daffodil.storage.model.StorageResult;
import com.daffodil.storage.properties.SeaweedfsProperties;
import com.daffodil.storage.properties.StorageProperties;
import com.daffodil.storage.service.AStorageService;
import com.daffodil.storage.util.StorageUtils;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * SeaweedFs分布式存储
 * @author yweijian
 * @date 2023年10月27日
 * @version 1.0
 * @description
 */
@Slf4j
@Service
@Configuration
@ConditionalOnProperty(name = StorageProperties.NAME, havingValue = StorageProperties.SEAWEEDFS)
public class StorageSeaweedfsServiceImpl extends AStorageService {

    @Autowired
    private StorageProperties storageProperties;

    @Autowired
    private SeaweedfsClient seaweedfsClient;

    /** 限定列举文件的最大个数 */
    private static final Integer MAXKEYS = 1000;

    /**
     * -SeaweedFS不支持分页查询，这里实现是按业务文件目录查询[伪分页]。
     * @example { folder: dev-upload/ name: *abc.jpg }
     * @remark 分页page.totalRow 总记录数只记录有效数据文件对象，不记录对象目录 
     */
    @Override
    public List<StorageResult> list(String folder, String name, Page page) {
        String prefix = StorageUtils.getPrefix(folder, null);
        SeaweedfsProperties properties = storageProperties.getSeaweedfs();

        DirPath dirPath = seaweedfsClient.list(prefix, name, MAXKEYS);

        List<StorageResult> results = new ArrayList<StorageResult>();
        if(null == dirPath || ObjectUtil.isEmpty(dirPath.getEntries())) {
            return results;
        }

        //目录数据
        List<DirEntrie> folders = dirPath.getEntries().stream().filter(entry -> entry.getFileSize() <= 0).collect(Collectors.toList());
        for(DirEntrie entrie : folders) {
            String key = entrie.getFullPath();
            StorageResult result = StorageResult.builder().name(FileNameUtil.getName(key)).origin(FileNameUtil.getName(key))
                    .prefix(FileNameUtil.getPrefix(key)).suffix(FileNameUtil.getSuffix(key)).size(0L).mime(StrUtil.EMPTY).key(key)
                    .url(properties.getDomain() + StrUtil.SLASH + key).metadata(MapUtil.empty()).build();
            results.add(result);
        }

        //对象数据
        List<DirEntrie> files = dirPath.getEntries().stream().filter(entry -> entry.getFileSize() > 0).collect(Collectors.toList());
        page.setTotalRow(files.size());
        int fromIndex = page.getFromIndex();
        int toIndex = Math.min(page.getToIndex(), files.size());
        if(fromIndex > toIndex) {
            return results;
        }
        
        for(int i = fromIndex; i < toIndex; i++) {
            DirEntrie entrie = files.get(i);
            String key = entrie.getFullPath();
            StorageResult result = StorageResult.builder().name(FileNameUtil.getName(key)).origin(FileNameUtil.getName(key))
                    .prefix(FileNameUtil.getPrefix(key)).suffix(FileNameUtil.getSuffix(key)).size(entrie.getFileSize()).mime(entrie.getMime()).key(key)
                    .url(properties.getDomain() + key).metadata(MapUtil.empty()).build();
            results.add(result);
        }

        return results;
    }

    @Override
    protected StorageResult upolad(StorageResult result, InputStream inputStream) {
        log.debug("SeaweedFS对象存储 > 上传对象 > fileName={}", result.getOrigin());

        SeaweedfsProperties properties = storageProperties.getSeaweedfs();
        try {
            DirResult dirResult = seaweedfsClient.upload(inputStream, result.getKey(), result.getName());
            result.setKey(dirResult.getFid());
            result.setUrl(properties.getDomain() + StrUtil.SLASH + dirResult.getFid());
            result.setETag(dirResult.getETag());
            log.debug("SeaweedFS对象存储 > 上传对象 > eTag={}", dirResult.getETag());
        }catch (SeaweedfsException e) {
            log.error(e.getMessage(), e);
            throw new BaseException(e.getCode(), e.getMessage());
        }

        log.debug("SeaweedFS对象存储 > 上传对象 > result={}", result.toString());
        return result;
    }

    @Override
    public void download(String key, HttpServletResponse response) {
        DirFile dirFile = seaweedfsClient.get(key);
        if(null != dirFile) {
            String fileName = dirFile.getFilename();

            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(fileName, StandardCharsets.UTF_8).build().toString());
            try {
                IOUtils.copy(dirFile.getInputStream(), response.getOutputStream());
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new BaseException(e.getMessage());
            }
        }
    }

    @Override
    public void remove(String key) {
        seaweedfsClient.delete(key);
    }

}
